<?php
include 'sql.php';
// 全局变量定义
$lazhu_json_string = array();  // 蜡炬图
$AllTrade = array();
$TradeDate;
$start_date_index_array = array();
$end_date_index_array = array();
$s_array = array();
$start_time_array = array();
$end_time_array = array();
$MonthlyReturns_array = array();
$Test_date = array();
$monthxishu = array();
$k1 = array();
$k2 = array();
$sx_rate = 0.0005;         // 手续费率  万分之五
$yh_rate = 0.001;          // 印花费率 千分之一   
$Money = (float)1000000;        // 可买卖的金钱总额
$newSql = new SQLClass();    // 先连接数据库
$TradeDate = $newSql->getTradeTime();  // 将交易日期存在数组中


/**********************************************************************
 * 功能：获取指定日期的所有满足条件的 ： 股票代码，开盘价格，开盘时间
 * @param $Day 指定日期
 * @return 股票代码，开盘价格，开盘时间
 *********************************************************************/
function selectOneDayInfor($Day, $orderRule)
{
    // echo "ada".$orderRule."<br/>";
    global $newSql;
    $result = $newSql->selectCode($Day, $orderRule);  // 调用sql
    if ($result != null) {
        return $result;
    }
}


/**********************************************************************
 * 功能：根据$股票代码,$卖出时间  获取卖出价
 * @param $Day 指定日期
 * @return $卖出时间 获取卖出价
 *********************************************************************/
function afterHoldTime($TsCode, $SellTime)
{
    global $newSql;
    // 调用函数，从数据库中获取数据:
    $result = $newSql->selectSellInfor($TsCode, $SellTime);
    if ($result != null) {
        $close = $result[0]["close"];
        $trade_date = $result[0]["trade_date"];
        $result2 = array("Close" => $close, "trade_date" => $trade_date);
        return $result2;
    } else {
        $result2 = array();
        return $result2;
    }
}


/**********************************************************************
 * 功能：计算卖出的信息
 * @param 股票代码 开盘价 买入时间 持股周期
 * @return  
 *********************************************************************/
function SellPrice($TsCode, $Open, $StartTime, $HoldTime, $KeepHoldProfit, $HighestFall, $StopLoseProfit)
{

    global $TradeDate;
    // 先找到什么时候开市
    while (array_search($StartTime, $TradeDate) == null) {
        $time = strtotime($StartTime);
        $StartTime = date("Y-m-d", strtotime("+1 day", $time));
    }
    $cur = array_search($StartTime, $TradeDate);  // 买入时间的下标
    $cur = $cur + 1;  // 开始可以卖出的第一天

    $TempHoldTime = $HoldTime - 1;
    if ($TempHoldTime == 0)
        $TempHoldTime++;
    // 这是需要止损情况
    while ($TempHoldTime) {
        // 传进空值
        // if($TempHoldTime== -1){
        //     break;
        // }
        $TempHoldTime = $TempHoldTime - 1;
        // var_dump($TradeDate[$cur]);
        $SellTime = $TradeDate[$cur];   // 开市时间
        $result = afterHoldTime($TsCode, $SellTime);
        while ($result == null)     // 这种情况是：股票有交易，但是这只股票没有交易？？？
        {
            $cur = $cur + 1;
            $SellTime = $TradeDate[$cur];  // 下一个交易日
            $result = afterHoldTime($TsCode, $SellTime);
        }
        $temp_close = $result["Close"];      //隐藏警告问题
        if ($Open == 0) {   // 开盘价为0的处理
            $Open = $Open + 0.1;
        }
        $temp_updownrate = (($temp_close - $Open) / $Open) * 100;      // 因为传进来的是百分制的数值
        if ($temp_updownrate < $StopLoseProfit) {
            $result2 = array("SellTime" => $SellTime, "Close" => $temp_close);
            return $result2;
        }
        $cur++;
    }
    // 正常涨跌幅,这里的temp_updownrate已经是正常卖出时间的
    if (@$temp_updownrate < @$KeepHoldProfit) {
        @$result2 = array("SellTime" => @$SellTime, "Close" => @$temp_close);
        return @$result2;
    }
    // 这是需要止盈情况
    else {
        $temp_max_updownrate = $temp_updownrate;
        $count = 500;
        while ($count--) {
            $SellTime = $TradeDate[$cur];        // 寻找下一天的开市时间	
            // $flag =0;
            // 因为前面循环有算过一次
            $result = afterHoldTime($TsCode, $SellTime);

            while ($result == null)     // 这种情况是：股票有交易，但是这只股票没有交易
            {
                $SellTime = $TradeDate[$cur];  // 下一个交易日
                $cur = $cur + 1;
                $result = afterHoldTime($TsCode, $SellTime);
            }
            $cur++;  // 防止多算
            $temp_close = $result["Close"];
            if ($Open == 0) {
                $Open = $Open + 0.1;
            }
            $temp_rate = (($temp_close - $Open) / $Open) * 100;      // 因为传进来的是百分制的数值
            if ($temp_max_updownrate - $temp_rate > $HighestFall) {  // 如果涨跌幅之差大于最高回落
                $result2 = array("SellTime" => $SellTime, "Close" => $temp_close);
                return $result2;
            } else {
                if ($temp_rate > $temp_max_updownrate) {
                    $temp_max_updownrate = $temp_rate;               // 更新涨跌幅的最大值
                }
            }
        }
    }
}


/********************************************************************* 
 * 功能：根据股票代码和买入时间获取相关的股票信息
 * parms：股票代码 买入时间
 * return：买入的相关信息：股票代码，买入时间，股票名称
 *********************************************************************/
function BuyInfor($TsCode, $BuyTime)
{
    global $newSql;
    $result = $newSql->select_Buyinfor($TsCode, $BuyTime);
    $data  =  array();
    $data["ts_code"] = $result["ts_code"];
    $data["open"] =  $result["open"];
    $data["trade_date"] = $result["trade_date"];
    $data["name"] = $result["name"];
    $data["close"] = $result["close"];
    return $data;
}

/********************************************************************* 
 * 功能：根据股票代码和买入时间获取相关的股票信息
 * parms：股票代码 买入时间
 * return：买入的相关信息：股票代码，买入时间，股票名称，vol，收盘价，最低价 最高价 
 *********************************************************************/
function BuyInfor2($TsCode, $BuyTime)
{
    global $newSql;
    $result = $newSql->select_Buyinfor2($TsCode, $BuyTime);
    $data  =  array();
    //$data["ts_code"] = $result["ts_code"];
    $data["trade_date"] = strtotime($result["trade_date"]) * 1000;
    $data["open"] =  (float)$result["open"];
    $data["high"] = (float)$result["high"];
    $data["low"] = (float)$result["low"];
    $data["close"] = (float)$result["close"];
    $data["vol"] = (float)$result["vol"];
    //转换为时间戳
    $xxx = strtotime($result["trade_date"]) * 1000;
    return $data;
}

/********************************************************************* 
 * 功能：根据股票代码与某个时间获取其前20天与后10天的数据
 * parms：股票代码 时间
 * return：
 *********************************************************************/
function get_information($ts_code, $day)
{
    global $TradeDate;
    $numb = 0;
    $cur = array_search($day, $TradeDate);  // 获得买入时间的下标
    // $cur1 = array_search($SellTime, $TradeDate);
    //得到前20天股票到信息
    for ($n = $cur; $n > $cur - 20; $n--) {
        $day = $TradeDate[$n];
        $LaZhu1[$numb] = BuyInfor2($ts_code, $day);
        $numb++;
    }

    //得到后10天到股票信息
    for ($n = $cur; $n < $cur + 10; $n++) {
        $day = $TradeDate[$n];
        $LaZhu2[$numb] = BuyInfor2($ts_code, $day);
        $numb++;
    }
    $AllLaZhu = $LaZhu1 + $LaZhu2;   // 合到一个数组
    return $AllLaZhu;
}


/**********************************************************************
 * 功能：主要根据 下面的参数，返回股票数量为：num<=单日买入数
 * parms： 回测开始时间 持股周期  单日买入数 卖出约束
 * return：股票数量为：num<=单日买入数
 *********************************************************************/
function OneTrade($StartTime, $HoldTime, $DayBuy,  $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule)
{

    global $TradeDate;
    $AllTrade = array();
    // 先找到什么时候开市
    while (array_search($StartTime, $TradeDate) == null) {
        $time = strtotime($StartTime);
        $StartTime = date("Y-m-d", strtotime("+1 day", $time));
    }
    $cur = array_search($StartTime, $TradeDate);  // 找到开市那天的下标
    $StartTime = $TradeDate[$cur];  // 开市查询股票
    $ChoosedStocks = selectOneDayInfor($StartTime, $orderRule);
    while ($ChoosedStocks == null) {
        $cur++;
        $StartTime = $TradeDate[$cur];
        $ChoosedStocks = selectOneDayInfor($StartTime, $orderRule);
    }
    // 开始买入的时间// 
    $cur = $cur + 1;   // 真正买入
    $BuyTime = $TradeDate[$cur];  // 真正开始买入的时间
    $NumOfStocks = count($ChoosedStocks);  // 选择的股票个数
    for ($i = 0; $i < $NumOfStocks; $i++) {
        // 获取买入信息
        $BuyInfor = BuyInfor($ChoosedStocks[$i]['ts_code'], $BuyTime);      //优化：先查temp、表
        while ($BuyInfor['ts_code'] == null) {          // 为什么不是$buyInfor ==null
            $cur++;
            $BuyTime = $TradeDate[$cur];
            $BuyInfor = BuyInfor($ChoosedStocks[$i]['ts_code'], $BuyTime);
        }

        // 获取卖出信息
        $SellInfor = SellPrice(
            $BuyInfor['ts_code'],
            $BuyInfor['open'],          // 开盘价
            $BuyInfor['trade_date'],  // 买入时间
            $HoldTime,
            $KeepHoldProfit,
            $HighestFall,
            $StopLoseProfit
        );
        // 对于开盘价为0的处理
        if ($BuyInfor['open'] == 0) {
            $shouyilv = 0;
        } else {
            $shouyilv = ($SellInfor['Close'] - $BuyInfor['open']) / $BuyInfor['open'];
            $shouyilv = round($shouyilv, 4) * 100;
        }
        $OneTrade = array(
            "ts_code" => $BuyInfor['ts_code'], 'BuyTime' => $BuyInfor['trade_date'],
            "Open" => $BuyInfor['open'], "Close" => $SellInfor['Close'], "SellTime" => $SellInfor['SellTime'],
            "shouyilv" => $shouyilv, "name" => $BuyInfor['name']
        );
        array_push($AllTrade, $OneTrade);
    }
    $NumOfAllTrade = count($AllTrade);
    // 为了保证单日买入数的条件，最多只能返回$DayBuy数量的股票交易
    $RealTrade = array();
    if ($NumOfAllTrade > $DayBuy) {    // 为了保证单日买入数的条件，
        for ($i = 0; $i < $DayBuy; $i++) {
            array_push($RealTrade, $AllTrade[$i]);
        }
        return $RealTrade;
    }
    return $AllTrade;
}


/**********************************************************************
 * 功能：根据指定的"键"(主要是指卖出时间)，对关联数组排序（升序）
 **********************************************************************/
function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row) {
        $sortarray[$key] = $row[$subfield];
    }
    array_multisort($sortarray, SORT_ASC, $array);
}

/**********************************************************************
 * 功能：根据指定的"键"(主要是指买入时间)，对关联数组排序（降序序）
 **********************************************************************/
function sort_array_of_array_desc(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row) {
        $sortarray[$key] = $row[$subfield];
    }
    array_multisort($sortarray, SORT_DESC, $array);
}

/**********************************************************************
 * 功能：对于一个已经升序排序过的数组，获取该数组中最小值的个数（用于买入）
 * parms ： 数组
 * return ：最小值的个数
 **********************************************************************/
function numberOfMin($a)
{
    $num = 1;
    for ($i = 0; $i < count($a) - 1; $i++) {
        if ($a[$i + 1]["SellTime"] == $a[$i]["SellTime"])
            $num++;
        else break;
    }
    return $num;
}


/**********************************************************************
 * 功能：根据开始时间，持股上限，单日买入数等条件
 * 		获取数量为$HoldLimit，且满足单日买入数的股票
 * parms ：回测输入的参数
 * return ：在满足单日买入数的前提下，数量为$HoldLimit的股票
 **********************************************************************/
function oneChooseStock($StartTime, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule)
{
    $time = $StartTime;
    $temp = array();
    global $TradeDate;
    // 根据条件的买入时间获取股票
    $a = OneTrade($time, $HoldTime, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule);
    $num_a = count($a);  // 此时的股票数量 <= $DayBuy
    $time = $a[0]["BuyTime"];  // 真正的买入时间
    $cur = array_search($time, $TradeDate);   // 寻找下标
    for ($i = 0; $i < $num_a; $i++) {
        array_push($temp, $a[$i]);
    }
    // 如果股票数量不足持股上限
    while (count($temp) < $HoldLimit) {
        $time = $TradeDate[$cur];
        $a = OneTrade($time, $HoldTime, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule);
        $num_a = count($a);
        $numtemps = count($temp);
        // 这是为了保证不共同持仓,,,,,,,,,,,,,,,,,,,,,,,,
        for ($i = 0; $i < $num_a; $i++) {
            $flag = 0;
            for ($j = 0; $j < count($temp); $j++) {
                if ($a[$i]["ts_code"] == $temp[$j]["ts_code"]) {
                    $flag++;
                }
            }
            if ($flag == 0) {
                array_push($temp, $a[$i]);
            }
        }
        $time = $a[0]["BuyTime"];  // 真正的买入时间		
        $cur = array_search($time, $TradeDate);
    }
    $NumOfTmep = count($temp);
    // 如果买入的股票数量超出了单日买入数，则要减去超出部分
    if (count($temp) > $HoldLimit) {
        for ($i = 0; $i < $NumOfTmep - $HoldLimit; $i++) {
            array_pop($temp);
        }
    }
    return $temp;
}

/**********************************************************************
 * 功能：将原本三维的数组转换成二维的数组
 **********************************************************************/
function reduceDimen($x)
{
    $xx = array();
    foreach ($x as $item) {
        for ($i = 0; $i < count($item); $i++) {
            array_push($xx, $item[$i]);
        }
    }
    return $xx;
}

/**********************************************************************
 * 功能：相当于是主函数，根据回测条件（一种持股周期下），得到选股（即：选了哪些股票）
 * @param：回测的条件
 * @return：一种持股周期下的选出的所有股票
 **********************************************************************/
function  OneAction($StartTime, $EndTime, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule, $addBorder)
{
    global $newSql;
    global $TradeDate;
    $TradeHIstory = array();    // 记录交易历史
    $time = $StartTime;
    $orderRuleBy = $orderRule;   // 转成全局
    // 先获取第一次交易
    if ($addBorder == NULL) {
        $TradeHIstory = oneChooseStock($time, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule);
        $TempSelect = $TradeHIstory;  // 临时存取

        /***************************获取第一次卖出时间以及应该买入的数量********************************* */
        sort_array_of_array($TempSelect, "SellTime");     // 按照卖出时间升序排序
        $NumOfMin = numberOfMin($TempSelect);             // 最小的个数

        $time = $TempSelect[0]["SellTime"];   // 最早卖出时间
        sort_array_of_array_desc($TempSelect, "BuyTime"); // 按照买入时间降序排序
        $NextBuyTime = $TempSelect[0]["BuyTime"];         // 最晚买入

        // 下一下的最早买入必须比前一次的最晚买入大
        if ($time < $NextBuyTime) {
            $time = $NextBuyTime;
        }
        $LeftAfterSell = array();     // 卖出持股到期的股票后，剩下的股票
        sort_array_of_array_desc($TempSelect, "SellTime"); // 卖出最晚排序
        $NumOfLeftAfterSell = $HoldLimit - $NumOfMin;
        if ($NumOfLeftAfterSell > 0) {
            for ($i = 0; $i < $HoldLimit - $NumOfMin; $i++) {
                array_push($LeftAfterSell, $TempSelect[$i]); // 卖出持股到期的股票后，剩下的股票
            }
        }
    } else {  // 如果存在原有持仓
        $numOfAdd = count($addBorder);       // 原有持仓个数 
        $NumOfMin = $HoldLimit - $numOfAdd;  // 卖出个数
        $LeftAfterSell = array();     // 卖出持股到期的股票后，剩下的股票
        for ($i = 0; $i < $numOfAdd; $i++) {
            array_push($LeftAfterSell, $addBorder[$i]); // 卖出持股到期的股票后，剩下的股票
            array_push($TradeHIstory, $addBorder[$i]); // 卖出持股到期的股票后，剩下的股票
        }
    }

    while ($time < $EndTime) {
        $select = oneChooseStock($time, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule);
        $TempSelect = array();
        $NumOfLeft = count($LeftAfterSell);
        if ($NumOfLeft == 0) { // 如果之前没有剩
            for ($i = 0; $i < count($select); $i++) {
                array_push($TempSelect, $select[$i]);    // 将选择的股票
                array_push($LeftAfterSell, $select[$i]);   // 将选择的股票放入
            }
        } else {
            for ($i = 0; $i < count($select); $i++) {
                $flag = 0;
                for ($j = 0; $j < $NumOfLeft; $j++) {
                    if ($select[$i]["ts_code"] == $LeftAfterSell[$j]["ts_code"]) {
                        $flag++;
                    }
                }
                if ($flag == 0) {
                    array_push($TempSelect, $select[$i]);    // 将选择的股票
                    array_push($LeftAfterSell, $select[$i]);   // 将选择的股票放入
                    $NumOfLeft++;
                }
            }
        }
        $sortTemp = $TempSelect;
        sort_array_of_array_desc($sortTemp, "BuyTime");     // 按照买入时间降序排序
        $time =  $TempSelect[0]["BuyTime"];
        $cur = array_search($time, $TradeDate);   // 寻找交易时间的下标

        //如果获取的股票数目不足，则选择下一天购买
        while (count($TempSelect) < $NumOfMin) {
            $time = $TradeDate[$cur];
            $select = oneChooseStock($time, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule);
            $NumOfLeft = count($LeftAfterSell);
            for ($i = 0; $i < count($select); $i++) {
                $flag = 0;
                for ($j = 0; $j < $NumOfLeft; $j++) {
                    if ($select[$i]["ts_code"] == $LeftAfterSell[$j]["ts_code"]) {
                        $flag++;
                    }
                }
                if ($flag == 0) {
                    array_push($TempSelect, $select[$i]);    // 将选择的股票
                    array_push($LeftAfterSell, $select[$i]);   // 将选择的股票放入
                    $NumOfLeft++;
                }
            }
            $sortTemp = $TempSelect;
            sort_array_of_array_desc($sortTemp, "BuyTime");     // 按照卖出时间升序排序
            $time =  $TempSelect[0]["BuyTime"];
            $cur = array_search($time, $TradeDate);   // 寻找下标
        }
        for ($i = 0; $i < $NumOfMin; $i++) {
            $next = $TempSelect[$i];
            array_push($TradeHIstory, $next);
        }
        $GetLast = array_slice($TradeHIstory, -$HoldLimit);
        sort_array_of_array($GetLast, "SellTime"); // 排序
        $NumOfMin = numberOfMin($GetLast);
        $time = $GetLast[0]["SellTime"];
        sort_array_of_array_desc($GetLast, "BuyTime"); // 排序
        $NextBuyTime = $GetLast[0]["BuyTime"];
        if ($time < $NextBuyTime) {
            $time = $NextBuyTime;
        }
        $LeftAfterSell = (array)null;   //数组清空
        sort_array_of_array_desc($GetLast, "SellTime"); // 排序

        $NumOfLeftAfterSell = $HoldLimit - $NumOfMin;
        if ($NumOfLeftAfterSell > 0) {
            for ($i = 0; $i < $HoldLimit - $NumOfMin; $i++) {

                array_push($LeftAfterSell, $GetLast[$i]);
            }
        }
    }
    return $TradeHIstory;
}

/*************************************************
 * function：获取购买的历史明细
 */
function  oneActionMore($StartTime, $EndTime, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule, $addBorder)
{
    $getFromOneAction = OneAction($StartTime, $EndTime, $HoldTime, $HoldLimit, $DayBuy, $KeepHoldProfit, $HighestFall, $StopLoseProfit, $orderRule, $addBorder);
    $lastCur = count($getFromOneAction) - 1;
    for ($i = $lastCur; $i >= 0; $i--) {
        if ($getFromOneAction[$i]['BuyTime'] >= $EndTime) {
            array_pop($getFromOneAction);
        } else {
            continue;
        }
    }
    return $getFromOneAction;
}

/***************************************************
 * function：根据历史明细，获得在回测结束时间段的持仓的股票
 * 如：2016-03-20为结束时间，则卖出时间为03-21与03-22的股票为持仓的股票
 * 注意：一定是先调用onemoreaction函数，获得了历史明细才能调用该函数
 * 因为这个函数有一个参数为历史明细$getFromOneAction
 */
function  leftMore($StartTime, $EndTime, $getFromOneAction)
{
    $chicang = NULL;
    if ($getFromOneAction != NULL) {
        $lastCur = count($getFromOneAction) - 1;
        for ($i = $lastCur; $i >= 0; $i--) {
            if ($getFromOneAction[$i]['SellTime'] > $EndTime) {
                @$chicang[] = $getFromOneAction[$i];
            } else {
                break;
            }
        }
    }
    return @$chicang;
}

/****************************************************************************************** */
// 统计涨跌幅范围的数目
function calNumOfUpdownRate($One_HoldLimit_Trade)
{
    $Bew0to35 = 0;
    $Bew35to75 = 0;
    $big75 = 0;
    $Bew_35to0 = 0;
    $Bew_75to_35 = 0;
    $small_75 = 0;
    foreach ($One_HoldLimit_Trade as $item) {
        if ($item['shouyilv'] < -7.5)
            $small_75++;
        else if ($item['shouyilv'] >= -7.5 && $item['shouyilv'] < -3.5)
            $Bew_75to_35++;
        else if ($item['shouyilv'] >= -3.5 && $item['shouyilv'] < 0)
            $Bew_35to0++;
        else if ($item['shouyilv'] >= 0 && $item['shouyilv'] < 3.5)
            $Bew0to35++;
        else if ($item['shouyilv'] >= 3.5 && $item['shouyilv'] < 7.5)
            $Bew35to75++;
        else if ($item['shouyilv'] >= 7.5)
            $big75++;
    }
    $com = array(
        "Bew0to35" => $Bew0to35, "Bew35to75" => $Bew35to75, "big75" => $big75, "Bew_35to0" => $Bew_35to0, "Bew_75to_35" => $Bew_75to_35, "small_75" => $small_75
    );
    return $com;
}

// 统计指标
function calTarget($One_HoldLimit_Trade, $hold_time_array, $HoldLimit, $StarTime, $EndTime, $stock_left)
{
    // var_dump($StarTime, $EndTime);
    $aaaa = calculate_caption($One_HoldLimit_Trade, $HoldLimit, $StarTime, $EndTime, $stock_left);
    $aaaa = $aaaa["one"];
    // 输出每日市值
    //var_dump($aaaa);
    global $TradeDate;
    $total = $One_HoldLimit_Trade;  // 临时的全局表 (购买顺序的总队列)  

    while (array_search($StarTime, $TradeDate) == null) {
        $time = strtotime($StarTime);
        $StarTime = date("Y-m-d", strtotime("+1 day", $time));
    }
    $start_date_index = array_search($StarTime, $TradeDate);
    // $end_date_index = array_search($total[count($total) - 1]['SellTime'], $TradeDate);

    while (array_search($EndTime, $TradeDate) == null) {
        $time = strtotime($EndTime);
        $EndTime = date("Y-m-d", strtotime("-1 day", $time));
    }
    $end_date_index = array_search($EndTime, $TradeDate);  // 获取下标
    // 添加判断 回测时就按
    if ($end_date_index <= $start_date_index) {
        return null;
    }

    $Total_revenue = 0.00;        //定义总收益
    $Principal = current($aaaa);    //定义本金
    $temp = 0.00;
    /*var_dump($start_date_index);
    var_dump($start_date_index+1);
    var_dump($TradeDate[$start_date_index]);
    var_dump($aaaa[$TradeDate[$start_date_index]]);*/
    //var_dump($aaaa);

    $Maxdrawdown = ($aaaa[$TradeDate[$start_date_index]] - $aaaa[$TradeDate[$start_date_index + 1]]) / $aaaa[$TradeDate[$start_date_index]];   //最大回撤
    $Annualized_rate_of_return = 0.00;            //定义年化收益率
    $Real_rate_of_return = 0.00;
    //定义真实收益率
    /*日期循环
	for($i= $start_date_index;$i<=$end_date_index;$i++)
	{
		var_dump($TradeDate[$i]);				
	}*/

    /*市值循环
	for($i= $start_date_index;$i<=$end_date_index;$i++)
	{
		var_dump($aaaa[$TradeDate[$i]]);				
	}*/

    #最大回撤：最大回撤是指max((Di-Dj)/Di)，其中D为某一天的净值，i为某一天，j为i后的某一天
    for ($i = $start_date_index; $i <= $end_date_index; $i++) {
        for ($j = $i; $j <= $end_date_index; $j++) {

            $temp = ($aaaa[$TradeDate[$i]] - $aaaa[$TradeDate[$j]]) / $aaaa[$TradeDate[$i]];
            if ($temp > $Maxdrawdown && $temp != 0) {
                $Maxdrawdown = $temp;
            }
        }
    }
    $Maxdrawdown = $Maxdrawdown * 100;
    $Maxdrawdown = round($Maxdrawdown, 2);
    //var_dump("$Maxdrawdown");
    //var_dump($aaaa['2016-03-11']);
    $bbbb = end($aaaa);        //最后一天的市值
    /*foreach($aaaa as $value)
		{
			echo "$value <br>";
			$Total_revenue = $Total_revenue + $value;
		}*/
    $Total_revenue = $bbbb - $Principal;        //总收益=最后一天的市值-本金
    //var_dump($Total_revenue);
    // 下面的变量主要是为了回测分析使用的。
    global $Money;
    $ave_shouyi = 0;        // 平均收益率
    $max_shouyi = -10000;     // 最大收益率
    $min_shouyi = 10000;     // 最小收益率
    $trade_times = 0;       // 交易次数
    $all_updown = 0;        // 一次交易的总收益率（即收益率之和）
    $year_shouyi = 0;       // 年化收益率
    $max_huice = -1000;
    $xiapu_rate = 0;
    $Mar_rate = 0;
    $getShouyi = array();    // 获取每次交易

    foreach ($One_HoldLimit_Trade as $item) {

        // 计算收益最大，最小，总收益
        array_push($getShouyi, $item['shouyilv']);
        $trade_times++;
        $all_updown += $item['shouyilv'];
        $all_updown = round($all_updown, 2);
        // $max_shouyi = max($max_shouyi, $item['shouyilv']);
        if ($item['shouyilv'] > $max_shouyi && $item['shouyilv'] != 0)
            $max_shouyi = $item['shouyilv'];
        if ($item['shouyilv'] < $min_shouyi && $item['shouyilv'] != 0)
            $min_shouyi = $item['shouyilv'];
        // $min_shouyi = min($min_shouyi, $item['shouyilv']);
    }

    /**先假设年化收益率为真实收益率************************** */
    $year_shouyi = $all_updown;
    //真实收益率 = 总收益/本金
    $Real_rate_of_return = $Total_revenue / $Principal * 100;
    $Real_rate_of_return = round($Real_rate_of_return, 2);
    // var_dump($Real_rate_of_return);

    //计算年化收益率 年化收益率 = 总收益 / 本金 * 365 / 本次交易总天数
    $startdate1 = $_REQUEST["sta_test"];        //利用 str_replace 去掉日期中的"-"
    $startdate11 = str_replace("-", "", $startdate1);
    $enddate1 = $_REQUEST["end_test"];
    $enddate11 = str_replace("-", "", $enddate1);
    //$ddaayy = $enddate11 - $startdate11 + 1; 	//本次交易总天数
    $startdate111 = new DateTime($startdate1);
    $enddate111 = new DateTime($enddate1);
    $ddaayy = $enddate111->diff($startdate111)->days;
    $ddaayy = $ddaayy + 1;
    // var_dump ($ddaayy);
    //$Annualized_rate_of_return = $Total_revenue / $Principal * 365 / $ddaayy * 100;
    $Annualized_rate_of_return = $Total_revenue / $Principal * 244 / $ddaayy * 100;        //242-244天
    $Annualized_rate_of_return = round($Annualized_rate_of_return, 2);
    // var_dump($Annualized_rate_of_return);

    // 计算平均收益率
    $ave_shouyi = $all_updown / $trade_times;
    $ave_shouyi = round($ave_shouyi, 4);

    // 原最大回撤
    $max_huice = ($max_shouyi - $min_shouyi) / $max_shouyi;
    $max_huice = round($max_huice, 2);

    // Mar比率 Mar比率 = 年均回报率 / 最大的衰落幅度
    //$Mar_rate = ($year_shouyi / $max_huice) * 100;
    if ($Maxdrawdown != 0) {
        $Mar_rate = $Annualized_rate_of_return / $Maxdrawdown;
        $Mar_rate = round($Mar_rate, 2);
        // var_dump($Mar_rate);
    }
    // $Mar_rate = $Annualized_rate_of_return / $Maxdrawdown;
    // $Mar_rate = round($Mar_rate, 2);
    // // var_dump($Mar_rate);

    // 夏普比率中的标准差
    $cha_sum = 0;
    for ($i = 0; $i < count($getShouyi); $i++) {
        $cha_sum = $cha_sum + ($getShouyi[$i] - $ave_shouyi) * ($getShouyi[$i] - $ave_shouyi);
    }
    $chu = $cha_sum / count($getShouyi);
    $sigma = sqrt($chu);  // 标准差
    // 夏普比率=(年度平均复合增长率-无风险回报率)/回报率的标准差
    if ($sigma != 0) {
        $xiapu_rate = ($year_shouyi) / $sigma;
        # $xiapu_rate = $year_shouyi / $sigma;
        $xiapu_rate = round($xiapu_rate, 2);
    }


    //夏普比率重写 夏普比率 = （年化收益率 - 无风险回报率） / 每日收益率的标准差
    $Risk_free_rate_of_return = 0.020310;        //无风险收益利率
    $Daily_rate_of_earning = 0.00;        //每日收益率
    $cccc = calculate_caption($One_HoldLimit_Trade, $HoldLimit, $StarTime, $EndTime, $stock_left);        //准备用来存放每日的收益率
    $cccc = $cccc["one"];
    $cccc_sum = 0.00;        //每日收益率的和
    $cccc_ave = 0.00;        //每日收益率的平均值
    $cccc_variance = 0.00;         //每日收益率的方差
    $cccc_standard_deviation = 0.00;        //每日收益率的标准差
    $Sharpe_rate = 0.00;        //夏普比率
    $cccc[$TradeDate[$start_date_index]] = ($aaaa[$TradeDate[$start_date_index]] - $Principal) / $aaaa[$TradeDate[$start_date_index]];        //第一天的收益率
    for ($i = $start_date_index + 1; $i <= $end_date_index; $i++) {
        $Daily_rate_of_earning = ($aaaa[$TradeDate[$i]] - $aaaa[$TradeDate[$i - 1]]) / $aaaa[$TradeDate[$i]];
        $cccc[$TradeDate[$i]] = $Daily_rate_of_earning;
    }
    for ($i = $start_date_index; $i <= $end_date_index; $i++)
        $cccc_sum += $cccc[$TradeDate[$i]];
    $cccc_ave = $cccc_sum / count($cccc);
    for ($i = $start_date_index; $i <= $end_date_index; $i++)
        $cccc_variance += ($cccc[$TradeDate[$i]] - $cccc_ave) * ($cccc[$TradeDate[$i]] - $cccc_ave);
    $cccc_variance = $cccc_variance / count($cccc);
    $cccc_standard_deviation = sqrt($cccc_variance);
    //$Sharpe_rate = (($Annualized_rate_of_return / 100) - $Risk_free_rate_of_return) / $cccc_standard_deviation;	//（年化收益率 - 无风险利率）/每日收益率的标准差
    //var_dump($Sharpe_rate);
    //$Sharpe_rate = (($Real_rate_of_return / 100) - $Risk_free_rate_of_return) / $cccc_standard_deviation;		//（真实收益率 - 无风险利率）/每日收益率的标准差
    //var_dump($Sharpe_rate);
    if ($cccc_standard_deviation != null) {
        $Sharpe_rate = $Real_rate_of_return / 100 / $cccc_standard_deviation;
        $Sharpe_rate = round($Sharpe_rate, 2);
        //真实收益率 /每日收益率的标准差   
    }

    // // ////////////////////////////////////////////////////
    // global $start_date_index_array;
    // global $end_date_index_array;
    // global $monthxishu;
    // global $start_time_array;
    // global $end_time_array;
    // global $MonthlyReturns_array;
    // global $Test_date;
    // global $k1;
    // global $k2;
    // //切割回测开始、结束时间，获得年、月
    // $k1 = explode("-", $_REQUEST["sta_test"]);
    // $k2 = explode("-", $_REQUEST["end_test"]);

    // //计算时间逻辑
    // // 同年同月
    // if ($k1[0] == $k2[0] && $k1[1] == $k2[1]) {
    //     array_push($Test_date, $k1[0] . '-' . $k1[1] . '-' . $k1[2]);
    //     array_push($Test_date, $k1[0] . '-' . $k1[1] . '-' . $k2[2]);
    // }

    // //同年，不同月
    // if ($k1[0] == $k2[0] && $k1[1] < $k2[1]) {
    //     array_push($Test_date, $k1[0] . '-' . $k1[1] . '-' . $k1[2]);
    //     $date_year = $k1[0];
    //     for ($date_month = $k1[1] + 1; $date_month <= $k2[1] - 1; $date_month++) {
    //         array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //     }
    //     array_push($Test_date, $k2[0] . '-' . $k2[1] . '-' . $k2[2]);
    // }
    // /************************************************************************
    // K1是切割的时间，切割成3份，分别是年月日，K2同理  见790行
    // 1 和 4 已经可以正常跑了，现在 2 和 3 的情况，逻辑上有些问题，不知道怎么改；还有848行的 355 是应该改成经历的总月数么
    // 获取到对应日期之后，再执行你之前写的函数---向前找和向后找，找到有市值的那天，最后得到角标，
    // 然后代入到$aaaa中,获得当天的市值，再代入我906行开始，写的公式，计算出月回报率。
    //  **************************************************************************/
    // // 年份相差一年+++++++
    // if ($k2[0] - $k1[0] == 1) {
    //     array_push($Test_date, $k1[0] . '-' . $k1[1] . '-' . $k1[2]);
    //     $date_year = $k1[0];
    //     for ($date_month = $k1[1] + 1; $date_month <= 12; $date_month++) {
    //         // 第一年的月份
    //         array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //     }
    //     $date_year = $k2[0];
    //     for ($date_month = 1; $date_month <= $k2[1] - 1; $date_month++) {
    //         // 第二年的月份
    //         array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //     }
    //     array_push($Test_date, $k2[0] . '-' . $k2[1] . '-' . $k2[2]);
    // }

    // //4.(如果跨年份大于2年)
    // if ($k2[0] - $k1[0] > 1) {

    //     array_push($Test_date, $k1[0] . '-' . $k1[1] . '-' . $k1[2]);
    //     $date_year = $k1[0];
    //     for ($date_month = $k1[1] + 1; $date_month <= 12; $date_month++) {
    //         // 第一年的月份
    //         array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //     }
    //     for ($date_year = $k1[0] + 1; $date_year <= $k2[0] - 1; $date_year++) {
    //         for ($date_month = 1; $date_month <= 12; $date_month++) {
    //             array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //         }
    //     }
    //     $date_year = $k2[0];
    //     for ($date_month = 1; $date_month <= $k2[1] - 1; $date_month++) {
    //         // 第二年的月份
    //         array_push($Test_date, $date_year . '-' . $date_month . '-01');
    //     }
    //     array_push($Test_date, $k2[0] . '-' . $k2[1] . '-' . $k2[2]);
    // }
    // // var_dump($Test_date);
    // // var_dump(count($Test_date));
    // //////////////////////////////////////////////
    // // 开始 时间最开始的 另处理
    // $start_date_index = array_search($_REQUEST["sta_test"], $TradeDate);  // 获取当月第一天开盘日期下标
    // array_push($start_date_index_array, $start_date_index);
    // for ($i = 1; $i < count($Test_date); $i++) {
    //     $Test_StartTime = $Test_date[$i];
    //     // var_dump($Test_StartTime);
    //     // var_dump(array_search($Test_StartTime, $TradeDate));
    //     // var_dump(array_search('2016-04-01', $TradeDate));
    //     // 循环向下找到，当月开盘的第一天
    //     while (array_search($Test_StartTime, $TradeDate) == null) {
    //         if ($i != count($Test_date) - 1) {
                
    //             $time = strtotime($Test_StartTime);
    //             $Test_StartTime = date("Y-m-d", strtotime("+1 day", $time));
    //         } 
    //         if($i == count($Test_date) - 1){
    //             echo '???';
    //             $cnm = explode("-", $Test_StartTime);
    //             $time = $cnm[0] . '-' . $cnm[1] . '-01';
    //             $time = strtotime($Test_StartTime);
    //             $Test_StartTime = date("Y-m-d", strtotime("+1 day", $time));
    //         }
    //     }
    //     //var_dump($Test_StartTime);
    //     $start_date_index = array_search($Test_StartTime, $TradeDate);  // 获取当月第一天开盘日期下标
    //     array_push($start_date_index_array, $start_date_index);
        
    // }

    // //////////////////////////////////////////////////////////
    // // 结束时间 
    // for ($i = 1; $i < count($Test_date); $i++) {
    //     $Test_EndTime = $Test_date[$i];  // ??/
    //     while (array_search($Test_EndTime, $TradeDate) == null) {
    //         if ($i != count($Test_date) - 1) {
    //             $time = strtotime($Test_EndTime);
    //             $Test_EndTime = date("Y-m-d", strtotime("-1 day", $time));
    //         } else {
    //             $nmd = explode("-", $Test_StartTime);
    //             $time = $nmd[0] . '-' . $nmd[1] . '-01';
    //             $time = strtotime($Test_EndTime);
    //             $Test_EndTime = date("Y-m-d", strtotime("-1 day", $time));
    //         }
    //     }
    //     //var_dump($Test_EndTime);
    //     $end_date_index = array_search($Test_EndTime, $TradeDate);       // 获取当月最后一天日期下标
    //     array_push($end_date_index_array, $end_date_index);
    // }
    //     $end_date_index = array_search($_REQUEST["end_test"], $TradeDate);  // 获取当月第一天开盘日期下标
    //     array_push($end_date_index_array, $start_date_index);

    // // var_dump($TradeDate[end($end_date_index_array)]);
    // //  各356个
    // //var_dump($start_date_index_array);
    // //var_dump($end_date_index_array);
    // for ($i = 0; $i < count($start_date_index_array); $i++) {
    //     // 获得每个月的-月初时间-数组
    //     $b = $TradeDate[$start_date_index_array[$i]];
    //     //var_dump($b);
    //     array_push($start_time_array, $b);
    // }
    // ////
    // for ($i = 0; $i < count($end_date_index_array); $i++) {
    //     // 获得每个月的-月底时间-数组
    //     $c = $TradeDate[$end_date_index_array[$i]];
    //     // var_dump($c);
    //     array_push($end_time_array, $c);
    // }

    // ////
    // var_dump($start_date_index_array);
    // var_dump($end_date_index_array);


    // for ($i = 0; $i < count($start_date_index_array); $i++) {
    //     $n = $end_date_index_array[$i] - $start_date_index_array[$i];
    //     array_push($monthxishu, $n);
    // }

    // // //356个 角标之差 负责计算系数
    // // //var_dump($monthxishu);

    // // //测试写死的月度回报能否计算
    // // //    $MonthlyReturns = (($aaaa["2016-03-17"] - $aaaa["2016-03-14"]) / $aaaa["2016-03-14"])*(20/($monthxishu[233]))*100;
    // // //    var_dump($MonthlyReturns);

    // // //每月第一天有市值的日期//每月最后一天有市值的日期  356个
    // // //var_dump($start_time_array);
    // // //var_dump($end_time_array);
    // // // var_dump($aaaa);
    // // // var_dump($monthxishu);


    // for ($i = 0; $i <= count($start_time_array); $i++) {
    //     // var_dump($aaaa[$start_time_array[$i]]);
    //     // var_dump($monthxishu[$i]);
    //     $MonthlyReturns = (($aaaa[$end_time_array[$i]] - $aaaa[$start_time_array[$i]]) / $aaaa[$start_time_array[$i]]) * (20 / ($monthxishu[$i])) * 100;

    //     array_push($MonthlyReturns_array, $MonthlyReturns);
    // }
    //  //var_dump($MonthlyReturns_array);


    // ///////////////////////////////////////////////////////////////////






    
    // 将回测分析的指标保存到数组中，后面进行表格的显示
    $trade_analysis = array(
        "start_day" => $StarTime, "end_day" => $EndTime,
        "year_shouyi" => $Annualized_rate_of_return, "ave_shouyi" => $ave_shouyi, "max_shouyi" => $max_shouyi,
        "min_shouyi" => $min_shouyi, "trade_times" => $trade_times, "holdtime" => $hold_time_array,
        "real_shouyilv" => $Real_rate_of_return, "max_huice" => $Maxdrawdown, "xiapu_rate" => $Sharpe_rate, "Mar_rate" => $Mar_rate
    );
    //var_dump($trade_analysis);
    return $trade_analysis;
}


// 用于计算折线图数据的函数
function calForLinechart($One_HoldLimit_Trade)
{
    $LineChart = array();   // 用于显示折线图所需的数据
    $sum_shouyi = 0;
    foreach ($One_HoldLimit_Trade as $item) {
        // 用于计算累加收益率和时间戳的转换，此时不包含印花税
        $sum_shouyi += $item['shouyilv'];
        $sum_shouyi = round($sum_shouyi, 2);
        $Timestamp = strtotime($item['BuyTime']) * 1000;  // 时间=>时间戳
        $one = array($Timestamp, $sum_shouyi);     // 一次时间与累积收益率
        // 将每一种情况的累加收益率加入数组
        array_push($LineChart, $one);
    }
    array_pop($LineChart);  // 去掉最后一次交易的结果（因为那时持股）
    return $LineChart;
}


// 指数的相关计算
function zhishuData($compareWay, $Sta_Test, $End_Test)
{
    global $newSql;
    $zhishu_start = $Sta_Test;
    $zhishu_end = $End_Test;

    global $TradeDate;
    // 开始回测时间
    while (array_search($zhishu_start, $TradeDate) == null) {
        $time = strtotime($zhishu_start);
        $zhishu_start = date("Y-m-d", strtotime("+1 day", $time));
    }
    $cur1 = array_search($zhishu_start, $TradeDate);  // 买入时间的下标
    $zhishu_start = $TradeDate[$cur1];
    $zhishu_start = str_replace('-', '', $zhishu_start);

    // 结束回测时间
    while (array_search($zhishu_end, $TradeDate) == null) {
        $time = strtotime($zhishu_end);
        $zhishu_end = date("Y-m-d", strtotime("+1 day", $time));
    }
    $cur2 = array_search($zhishu_end, $TradeDate);  // 买入时间的下标
    $zhishu_end = $TradeDate[$cur2];
    $zhishu_end = str_replace('-', '', $zhishu_end);

    // 需要加一个 判空的判断
    $tt = $newSql->select_zhishu($compareWay, $zhishu_start, $zhishu_end);
    if ($tt == null) {
        return null;
    } else {
        foreach ($tt as $item) {
            $trade_time[] = strtotime($item['trade_date']) * 1000;  // 时间=>时间戳
            $close[] = (float)$item['close'];
        }
        for ($i = 0; $i < count($close); $i++) {
            $one[] = array($trade_time[$i], $close[$i]);     // 一次时间与累积收益率
        }
        return $one;
    }
}

/**
 * 上限股票开始排序
 *
 * @param [type] $full_num
 * @param [type] $total
 * @return void
 */
function sort_fullnum($full_num, $total)
{
    $a = [];
    for ($i = 0; $i < $full_num; $i++) {
        array_push($a, $total[$i]);
    }
    sort_array_of_array($a, "SellTime");
    return $a;
}

/**
 * 计算训练区间每日市值
 *
 * @param  $tempTotal 
 * @return 每日市值
 */// 存储amount
function  calculate_caption($tempTotal, $HoldLimit, $StarTime, $EndTime, $stock_left)
{
    global $TradeDate;  // 开盘时间 size = 3306
    $total = $tempTotal;  // 临时的全局表 (购买顺序的总队列)   
    $full_num = $HoldLimit;  //持股上限数量 3   
    $total_index = $HoldLimit; // 作为定位指针
    $buy_queue = new Linklist(); //持股上限 状态
    global $Money;
    $money = $Money;

    // $sx_rate = 0.0005;  // 手续费率  万分之五
    // $yh_rate = 0.001;  // 印花费率 千分之一    
    global $sx_rate;
    global $yh_rate;
    $rate = $sx_rate +  $yh_rate;

    //var_dump($total);
    $buy_flag = 0;  // 计算购买份额  设置flag 买 +1 卖-1   //买的队列  
    if (count($total) > 0) {
        /// 原版本保持持股上限为满
        if (count($total) >= $full_num) {
            $buy_list = sort_fullnum($full_num, $total);
            $buy_queue->addFirst($buy_list[0]);
            //定义 冗余 控制错误的数据 
            for ($i = 0; $i < $full_num - 1; $i++) {
                $buy_queue->add($i, $buy_list[$i + 1]);
            }
        } else {
            $buy_list = sort_fullnum(count($total), $total);
            $buy_queue->addFirst($buy_list[0]);
            for ($i = 0; $i < count($total) - 1; $i++) {
                $buy_queue->add($i, $buy_list[$i + 1]);
            }
        }
        // $node = new Linklist();
        // $node->addFirst($buy_list[0]);   // 插入头部（第一个）
        // $node->add(0,$buy_list[1]);     // 插入第2个位置
        // $node->add(1,$buy_list[2]);     //插入第3个位置
        // 用键值对存储每天的总市值  键(时间) - 值(市值= 现金 + 股值 ) money + temp_money
        while (array_search($StarTime, $TradeDate) == null) {
            $time = strtotime($StarTime);
            $StarTime = date("Y-m-d", strtotime("+1 day", $time));
        }

        $start_date_index = array_search($StarTime, $TradeDate);

        // 到结束的时就按
        // $end_date_index = array_search($total[count($total) - 1]['SellTime'], $TradeDate);
        // 往前查到 结束时间下标
        //var_dump(array_search($EndTime, $TradeDate) == null);
        //var_dump($EndTime);
        while (array_search($EndTime, $TradeDate) == null) {
            $time = strtotime($EndTime);
            $EndTime = date("Y-m-d", strtotime("-1 day", $time));
        }

        $end_date_index = array_search($EndTime, $TradeDate);  // 获取下标
        // 添加判断 回测时就按
        if ($end_date_index <= $start_date_index) {
            return null;
        }
        //存放每天的总市值 
        $daily_money = array();
        // 存放股票对应的 持股数量
        $stock_amount = array();
        $stock_amount = $stock_left;

        // 开始 结束 时间内循环
        for ($mm = $start_date_index; $mm <= $end_date_index; $mm++) {
            $now_time = $TradeDate[$mm];
            //var_dump($now_time);
            $temp_money = 0; //累加计算每天的 股票市值
            // 增加一个 对应时间买入的操作
            //$bqn = $temp_buy_queue->size;  // 持股队列循环
            for ($qq = 0; $qq < $buy_queue->size; $qq++) {
                // 买入
                if (strtotime($buy_queue->select($qq)['BuyTime']) == strtotime($now_time)) {
                    //根据 股票代码-时间 查找详细信息
                    $buy_info = BuyInfor($buy_queue->select($qq)['ts_code'], $now_time);
                    if ($buy_info['open'] < 0) break;

                    $buynum = $full_num - $buy_flag;
                    if ($buynum == 0) {
                        echo 'error';
                    }
                    $buy_li = buy((float)$buy_info['open'], $money, $buynum);
                    $buy_flag++;
                    // $stock_amount[$buy_info['ts_code']] = $buy_li['amount'];
                    $stock_amount[$buy_queue->select($qq)['ts_code']] = $buy_li['amount'];
                    $money = $buy_li['money'];
                    @$temp_money = $temp_money + (float)($buy_info['close'] * $stock_amount[$buy_info['ts_code']] * 100);
                    // echo "买的时候的钱";
                    // var_dump($money.'---'.$temp_money);
                }
                // 持仓
                if (
                    strtotime($buy_queue->select($qq)['BuyTime']) < strtotime($now_time)
                    && strtotime($now_time) < strtotime($buy_queue->select($qq)['SellTime'])
                ) {
                    $buy_info = BuyInfor($buy_queue->select($qq)['ts_code'], $now_time);

                    @$temp_money = $temp_money +  (float)($buy_info['close'] * $stock_amount[$buy_info['ts_code']] * 100);
                }
                // 卖出
                if (strtotime($buy_queue->select($qq)['SellTime']) == strtotime($now_time)) {
                    $buy_info = BuyInfor($buy_queue->select($qq)['ts_code'], $now_time);
                    // $stock_amount 的问题
                    @$amount = $stock_amount[$buy_info['ts_code']];
                    $money = sell($amount, $buy_info['close'], $money);
                    // var_dump($money);
                    $buy_flag--;
                    // 卖出 删除股票对应 stock_code - amount 
                    unset($stock_amount[$buy_info['ts_code']]);
                    // 持股状态变化 每sell 持仓出队和入队 一次

                    $buy_queue->deleteFirst();
                    $qq = -1;  // 卖出后继续从头遍历    

                    // 错误数据问题
                    for ($total_index; $total_index < count($total); $total_index++) {
                        if (
                            strtotime($total[$total_index]['BuyTime']) >= strtotime($now_time)
                            && $total[$total_index]['Open'] > 0
                        ) {
                            break;
                        }
                        // var_dump($total[$total_index]['ts_code']);
                    }

                    //持股队列 更新状态 
                    for ($m = 0; $m <= $buy_queue->size - 2; $m++) {
                        // 排序 判断相邻
                        if ($total_index == count($total)) {
                            break;
                        }

                        // var_dump($total);
                        // var_dump($total[$total_index]);
                        $buy_queue->select($m)['SellTime'];
                        if (strtotime($buy_queue->select($m)['SellTime']) >= strtotime($total[$total_index]['SellTime'])) {
                            if ($m == 0) {
                                $buy_queue->addFirst($total[$total_index]);
                            } else {
                                $buy_queue->add($m - 1, $total[$total_index]);
                            }
                            $total_index++;
                            break;
                        }
                        if ($total_index == count($total)) {
                            break;
                        }
                        if (
                            strtotime($buy_queue->select($m)['SellTime']) < strtotime($total[$total_index]['SellTime'])
                            && strtotime($buy_queue->select($m + 1)['SellTime']) >= strtotime($total[$total_index]['SellTime'])
                        ) {
                            $buy_queue->add($m, $total[$total_index]);
                            $total_index++;
                            break;
                        } else if ($buy_queue->size < $full_num)
                            // $node->add(1,$buy_list[2]);     //插入第3个位置  
                            // 插入最后一个位置
                            $buy_queue->add($full_num - 2, $total[$total_index]);
                        $total_index++;
                        break;
                    }
                }
            }
            // 一个持股队列循环后 计算市值
            $daily_money[$now_time] = $money + $temp_money * (1.0 - $rate);
            //var_dump('最后的钱'.'----'.$money.'----'.$temp_money* (1.0 - $rate));
            //var_dump($daily_money);
            // echo '持股队列是：';
            // for($i = 0;$i<$buy_queue->size;$i++){
            //     var_dump($buy_queue->select($i));
            // }

        }
        //每日市值 
        //  var_dump($total);
        $res = array("one" => $daily_money, "two" => $stock_amount);
        // echo '---------------------------';
        //var_dump($res);
        return $res;
    }
}
// 获取回测时间内的 每日市值
//$aaaa = calculate_caption();
/**
 * 购买 计算
 */
function buy($open, $money, $buynum)
{
    global $sx_rate;
    $buy_li = array();
    $amount = (int)($money / ($buynum * $open * (1 + $sx_rate) * 100));
    $money = $money - ($amount * 100 * $open  * (1.0 + $sx_rate));
    // var_dump(($amount * 100 * $open  * (1.0 + $sx_rate)).'买的钱');
    $buy_li['money'] = $money;
    $buy_li['amount'] = $amount;

    return $buy_li;
}
/**
 * 卖出 计算
 */
function sell($amount, $close, $money)
{
    global $sx_rate;
    global $yh_rate;
    $rate = $sx_rate +  $yh_rate;
    $money = $money + ($amount * 100 * $close * (1.0 - $rate));
    // var_dump(($amount * 100 * $close * (1.0 - $rate)).'卖的钱');
    return $money;
}
// 存放市值函数   每天记录    现金 + amont * close



//定义节点类
class Node
{
    public $val;
    public $next;

    public function __construct($val = null, $next = null)
    {
        $this->val = $val;
        $this->next = $next;
    }
}

/**链表
 * Class Linklist
 * @package app\models
 */
class Linklist
{
    public $head;           //头节点(默认一个虚拟头节点)
    public $size;           //长度

    public function __construct()
    {
        $this->head = new Node();
        $this->size  = 0;
    }

    //头插法
    public function addFirst($value)
    {
        //        $node = new Node($value);
        //        $node->next = $this->head;
        //        $this->head = $node;

        //简化
        $this->head = new Node($value, $this->head);
        $this->size++;

        //$this->add(0,$value);
    }

    /**指定索引位置插入
     * @param $index    位置
     * @param $value    值
     * @throws Exception
     */
    public function add($index, $value)
    {

        if ($index > $this->size)
            throw new Exception('超过链表范围');

        //    if( $index==0 ){
        //        $this->addFirst($value);
        //    }else{
        $prev = $this->head;
        for ($i = 0; $i < $index; $i++) {
            $prev = $prev->next;
        }

        $prev->next = new Node($value, $prev->next);
        //    }

        $this->size++;
    }

    /**尾插法
     * @param $value
     */
    public function addLast($value)
    {

        $this->add($this->size, $value);
    }


    /***
     * 编辑
     * @param $index
     * @param $value
     * @throws Exception
     */
    public function edit($index, $value)
    {
        if ($index > $this->size - 1)
            throw new Exception('超过链表范围');

        $prev = $this->head->next;
        for ($i = 0; $i <= $index; $i++) {
            if ($i == $index)
                $prev->val = $value;
            $prev = $prev->next;
        }
    }

    /**
     * 查询指定index 链表  (修改返回一个数组)
     * @param $index 
     * @return $prev->val
     * @throws Exception
     */
    public function select($index)
    {
        if ($index > $this->size - 1)
            throw new Exception('超过链表范围');

        // $prev = $this->head->next;  
        $prev = $this->head;
        for ($i = 0; $i <= $index; $i++) {
            if ($i == $index)
                return $prev->val;
            $prev = $prev->next;
        }
    }


    /**删除
     * @param $index
     * @throws Exceptionr
     */
    public function delete($index)
    {
        if ($index > $this->size - 1)
            throw new Exception('超过链表范围');

        $prev = $this->head;
        for ($i = 0; $i <= $index; $i++) {
            if ($i == $index) {
                $prev->next = $prev->next->next;
                break;
            }
            $prev = $prev->next;
        }
        $this->size--;
    }

    public function deleteFirst()
    {
        $index = 0;
        if ($index > $this->size - 1)
            throw new Exception('超过链表范围');

        $prev = $this->head;
        // $prev->next = $prev->next->next;
        $this->head = $prev->next;
        $this->size--;
    }
    /**检索值是否存在
     * @param $value
     * @return bool
     */
    public function iscontain($value)
    {
        $prev = $this->head->next;
        while ($prev) {

            if ($prev->val == $value) {
                return true;
            }
            $prev = $prev->next;
        }

        return false;
    }

    /**转换为字符串
     * @return string
     */
    public function tostring()
    {

        $prev = $this->head->next;

        $r = [];
        while ($prev) {
            $r[] = $prev->val;
            $prev = $prev->next;
        }
        return implode('->', $r);
    }

    /**
     * 删除指定的节点值
     * @param $value
     */
    public function removeFileds($value)
    {
        $prev = $this->head;
        while ($prev->next) {
            if ($prev->val == $value) {
                $prev->val = $prev->next->val;
                $prev->next = $prev->next->next;
            } else {
                $prev = $prev->next;
            }
        }
    }

    /**
     * 通过递归方式删除指定的节点值
     * @param $value
     */
    public function removeFiledsByRecursion($value)
    {
        $this->head = $this->removeByRecursion($this->head, $value);
        return $this->head;
    }

    public function removeByRecursion($node, $value, $level = 0)
    {
        if ($node->next == null) {
            $this->showDeeep($level, $node->val);
            return $node->val == $value ? $node->next : $node;
        }

        $this->showDeeep($level, $node->val);
        $node->next = $this->removeByRecursion($node->next, $value, ++$level);
        $this->showDeeep($level, $node->val);
        return $node->val == $value ? $node->next : $node;
    }

    /**
     * 显示深度
     * 帮助理解递归执行过程，回调函数执行层序遵循系统栈 
     * @param int $level 深度层级
     * @param $val
     * @return bool
     */
    public function showDeeep($level = 1, $val)
    {
        if ($level < 1) {
            return false;
        }

        while ($level--) {
            echo '-';
        }
        echo "$val\n";
    }
}

/**
 * 链表实现队列
 * Class LinkListQueue
 * @package app\models
 */
class LinkListQueue extends Linklist
{
    public $tail;    //尾节点

    /**
     * push 入队
     * @param $value
     */
    public function push($value)
    {

        if ($this->head->val == null) {
            $this->tail = new Node($value);
            $this->head = $this->tail;
        } else {
            $this->tail->next =  new Node($value);
            $this->tail = $this->tail->next;
        }
        $this->size++;
    }

    /**
     * pop  出队
     * @return null
     * @throws \Exception
     */
    public function pop()
    {
        if ($this->size <= 0)
            throw new \Exception('超过链表范围');
        $val = $this->head->val;
        $this->head = $this->head->next;

        $this->size--;
        return $val;
    }

    /**
     * 查看队首
     */
    public function checkHead()
    {
        return $this->head->val;
    }

    /**
     * 查看队尾
     */
    public function checkEnd()
    {
        return $this->tail->val;
    }



    /**
     * toString
     */
    public function toString()
    {
        $r = [];
        while ($this->head) {
            $r[] = $this->head->val;
            $this->head = $this->head->next;
        }
        return implode('->', $r);
    }
}



/**
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 */
